using System.Xml.Linq;
using dnlib.DotNet;
using dnlib.DotNet.Emit;

namespace dnspy_fixer {
	internal class Program {
		static void Main(string[] args) {
			Console.WriteLine("Use DnSpy by console!");
			DnSpyHelper.Init();

			Console.WriteLine("====load dll.");
			var doc = DnSpyHelper.Load("C:\\Users\\admin\\source\\repos\\testcode\\testcode\\bin\\Debug\\net8.0\\testcode.dll");


			Console.WriteLine("====dump dll.");
			var mod = doc.AssemblyDef.Modules;
			foreach (var m in mod) {
				Console.WriteLine("module=" + m.Name);
				foreach (var o in m.Types) {
					Console.WriteLine("  type=" + o.Name);
					foreach (var method in o.Methods) {
						Console.WriteLine("    method=" + method.Name);

					}

					var methods = o.Methods.ToArray();
					foreach (var method in methods) {

						FixMethod(method);
					}
					
				}
			}


			Console.WriteLine("====save dll.");

			var savename = doc.Filename.Replace(".dll", "_2.dll");
			DnSpyHelper.Save(doc, savename);
		}

		static void FixMethod(dnlib.DotNet.MethodDef method) {
			if (!method.IsInternalCall)
				return;

			//添加靜態函數
			TypeSig[] paramtypes = new TypeSig[method.Parameters.Count];
			for (var i = 0; i < method.Parameters.Count; i++) {
				paramtypes[i] = method.Parameters[i].Type;
			}
			var usermethod = new MethodDefUser(
				"_this_" + method.Name
				, MethodSig.CreateStatic(method.ReturnType, paramtypes)
				, MethodImplAttributes.InternalCall
				, MethodAttributes.Private);
			var newmethod = method.Module.UpdateRowId(usermethod);
			//newmethod.Name = "_this_" + method.Name;
			newmethod.IsStatic = true;
			newmethod.DeclaringType = method.DeclaringType;
			for(var i=0;i<newmethod.Parameters.Count;i++) {
				if (i == 0)
					newmethod.Parameters[i].Name = "_this";
				else
					newmethod.Parameters[i].Name = method.Parameters[i].Name;
			}
			//newmethod.ReturnType = method.ReturnType;
			//newmethod.IsInternalCall = true;


			//reset icall

			method.IsInternalCall = false;

			method.Body = new dnlib.DotNet.Emit.CilBody();
			method.Body.Instructions.Add(new dnlib.DotNet.Emit.Instruction(dnlib.DotNet.Emit.OpCodes.Nop));
			//method.Body.Instructions.Add(new dnlib.DotNet.Emit.Instruction(dnlib.DotNet.Emit.OpCodes.Ldarg_0));//add this
			//dnspy this 也在parameters 中
			for (var i = 0; i < method.Parameters.Count; i++) {
				if (i == 0)
					method.Body.Instructions.Add(dnlib.DotNet.Emit.Instruction.Create(dnlib.DotNet.Emit.OpCodes.Ldarg_0));
				else if (i == 1)
					method.Body.Instructions.Add(dnlib.DotNet.Emit.Instruction.Create(dnlib.DotNet.Emit.OpCodes.Ldarg_1));
				else if (i == 2)
					method.Body.Instructions.Add(dnlib.DotNet.Emit.Instruction.Create(dnlib.DotNet.Emit.OpCodes.Ldarg_2));
				else if (i == 2)
					method.Body.Instructions.Add(dnlib.DotNet.Emit.Instruction.Create(dnlib.DotNet.Emit.OpCodes.Ldarg_3));
				else {
					var p = method.Parameters[i];
					method.Body.Instructions.Add(dnlib.DotNet.Emit.Instruction.Create(dnlib.DotNet.Emit.OpCodes.Ldarg_S, p));
				}
			}
			method.Body.Instructions.Add(dnlib.DotNet.Emit.Instruction.Create(dnlib.DotNet.Emit.OpCodes.Call, newmethod));
			method.Body.Instructions.Add(new dnlib.DotNet.Emit.Instruction(dnlib.DotNet.Emit.OpCodes.Nop));
			method.Body.Instructions.Add(new dnlib.DotNet.Emit.Instruction(dnlib.DotNet.Emit.OpCodes.Ret));//return
			method.Body.UpdateInstructionOffsets();
			
		}
		static class DnSpyHelper {

			static dnSpy.Documents.DsDocumentService _DsDocumentService;
			public static void Init() {
				var sett = new dnSpy.Documents.DsDocumentServiceSettings();
				var docpro = new dnSpy.Documents.DefaultDsDocumentProvider();
				var d = new dnSpy.Debugger.DotNet.Metadata.RuntimeAssemblyResolver();
				var p = new dnSpy.Contracts.DnSpy.Metadata.ExportRuntimeAssemblyResolverAttribute();
				var lazy = new Lazy<dnSpy.Contracts.DnSpy.Metadata.IRuntimeAssemblyResolver, dnSpy.Contracts.DnSpy.Metadata.IRuntimeAssemblyResolverMetadata>(p);
				_DsDocumentService = new dnSpy.Documents.DsDocumentService(sett, [docpro], []);
			}
			public static dnSpy.Contracts.Documents.IDsDocument Load(string filename) {
				var docinfo = dnSpy.Contracts.Documents.DsDocumentInfo.CreateDocument(filename);
				var doc = _DsDocumentService.TryGetOrCreate(docinfo, true);
				return doc;
			}
			public static void Save(dnSpy.Contracts.Documents.IDsDocument doc, string filename = null) {
				var saveoption = new dnSpy.AsmEditor.SaveModule.SaveModuleOptionsVM(doc);
				saveoption.WritePdb = true;
				if (filename != null)
					saveoption.FileName = filename;

				var saver = new dnSpy.AsmEditor.SaveModule.ModuleSaver([saveoption]);
				saver.SaveAll();

			}
		}
	}
}
